﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DSS_XNA.Engine.Error;
using DSS_XNA.Engine.Log;
using System.IO;
using System.Xml;
using System.Reflection;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace DSS_XNA.Engine.Menu
{
    public class Menu
    {
        #region Properties

        public string Name
        {
            get { return _name; }
        }
        private string _name = String.Empty;

        public MenuOptionCollection Options
        {
            get { return _options; }
            set { _options = value; }
        }
        private MenuOptionCollection _options = new MenuOptionCollection();

        public Game1 Engine
        {
            get { return _engine; }
            set { _engine = value; }
        }
        private Game1 _engine = null;

        public IMenu MenuWindow
        {
            get { return _menuWindow; }
        }
        private IMenu _menuWindow = null;

        #endregion

        #region Public Methods

        public Menu()
        {
        }

        public Menu(string path, Game1 engine)
        {
            if (path == null)
                throw new DSSException("'path' is null.");
            if (path.Length == 0)
                throw new DSSException("'path' is zero-length.");
            if (engine == null)
                throw new DSSException("'engine' is null.");

            this.Load(path, engine);
        }

        public bool Load(string path, Game1 engine)
        {
            if (path == null)
                throw new DSSException("'path' is null.");
            if (path.Length == 0)
                throw new DSSException("'path' is zero-length.");
            if (engine == null)
                throw new DSSException("'engine' is null.");

            Logger.Log(String.Format("Loading menu: {0}", path), LogLevel.Major);

            this.Engine = engine;

            this.Destroy();

            this.LoadFromFile(path);

            Logger.Log(String.Format("Loaded menu: {0} - {1} options",
                this.Name, this.Options.Count), LogLevel.Major);

            return true;
        }

        public void Destroy()
        {
            if (this.Options.Count > 0)
            {
                this.Options.Clear();
            }
        }

        public void Render(SpriteBatch spriteBatch)
        {
            if (this.MenuWindow != null)
            {
                this.MenuWindow.Render(spriteBatch);
            }
        }

        public void Run(GameTime gameTime, KeyboardState ks, MouseState ms, GamePadState gs)
        {
            if (this.MenuWindow != null)
            {
                this.MenuWindow.Run(gameTime, ks, ms, gs);
            }
        }

        #endregion

        #region Private Methods

        private void LoadFromFile(string path)
        {
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(path);

                this._name = doc.SelectSingleNode("menu/name").InnerText;

                string className = doc.SelectSingleNode("menu/class").InnerText;

                XmlNodeList nodeOptions = doc.SelectNodes("menu/options/option");
                bool isASelectedOption = false;
                foreach (XmlNode node in nodeOptions)
                {
                    string text = node.Attributes["text"].InnerText;
                    string action = node.Attributes["action"].InnerText;
                    string actionName = node.Attributes["actionName"].InnerText;
                    bool isSelected = false;
                    if (node.Attributes["selected"] != null)
                    {
                        // make sure a valid boolean option is passed and it is true
                        if (bool.TryParse(node.Attributes["selected"].InnerText, out isSelected) && isSelected)
                            isASelectedOption = true;
                    }

                    MenuOption op = new MenuOption(text, action, actionName, isSelected);
                    this.Options.Add(op);
                }
                if (!isASelectedOption && this.Options.Count > 0)
                    this.Options[0].IsSelected = true;

                this.LoadWindow(className);
            }
            catch (Exception ex)
            {
                Logger.Log(String.Format("Failed to load menu Xml.\n\n{0}", ex), LogLevel.Major);
            }
        }

        private void LoadWindow(string className)
        {
            if (className == null)
                throw new ArgumentNullException("'className' is null.");
            if (className.Length == 0)
                throw new ArgumentOutOfRangeException("'className' is zero-length.");

            try
            {
                Assembly asm = Assembly.GetCallingAssembly();
                Type type = asm.GetType(className);
                if (type == null)
                    throw new DSSException(String.Format("Failed to find the menu class: {0}",
                        className));

                if (type.GetInterface(typeof(IMenu).FullName) == null)
                    throw new DSSException(String.Format("The menu class: {0} does not inherit from {1}",
                        className, typeof(IMenu)));

                ConstructorInfo con = type.GetConstructor(new Type[] { typeof(Game1), typeof(MenuOptionCollection) });

                this._menuWindow = con.Invoke(new object [] { this.Engine, this.Options } ) as IMenu;
                if (this._menuWindow == null)
                    throw new DSSException("'this._mainWindow' is null.");
            }
            catch (Exception ex)
            {
                Logger.LogCritical(String.Format("Failed to load menu class: {0}\n\n{1}",
                    className, ex));
            }
        }

        #endregion
    }
}
